/** \file Converter.cpp
    \author belot.nicolas
*/

#include "Converter.h"
#include <iostream>
namespace libtrckr {

Converter::Converter(double _ratio, int _size){
	insize =_size;
	infree=insize;
	outsize=_size*_ratio;
	outend=0;
	//we wait the last moment to allocate in and out...
	in = 0;
	out = 0;
	#ifdef HAVE_SAMPLERATE 
		int error;
		info = src_new(SRC_SINC_BEST_QUALITY, 1, &error);
		std::cout << src_strerror(error) << std::endl;
		ios = (SRC_DATA*)malloc(sizeof(SRC_DATA));
		ios->src_ratio = _ratio;
	#endif
}

Converter::Converter(const Converter& c): insize(c.insize),outsize(c.outsize){
	in =0;
	out =0;
	infree=insize;
	outend=0;
	#ifdef HAVE_SAMPLERATE
		int error;
		info = src_new(SRC_SINC_BEST_QUALITY, 1, &error);
		std::cout << src_strerror(error) << std::endl;
		ios = (SRC_DATA*)malloc(sizeof(SRC_DATA));
		ios->src_ratio = c.ios->src_ratio;
		ios->data_in=0;
		ios->data_out=0;
	#endif
}

Converter& Converter::operator=(const Converter& c){
	in=0;
	out=0;
	insize=c.insize;
	outsize=c.outsize;
	infree=insize;
	#ifdef HAVE_SAMPLERATE
		int error;
		info = src_new(SRC_SINC_BEST_QUALITY, 1, &error);
		std::cout << src_strerror(error) << std::endl;
		ios = (SRC_DATA*)malloc(sizeof(SRC_DATA));
		ios->src_ratio = c.ios->src_ratio;
		ios->data_in=0;
		ios->data_out=0;
	#endif
	return *this;
}

Converter::~Converter(){
	if(in!=0){
		 free(in);
	
	}
	if(out!=0){
		 free(out);
	
	}
	#ifdef HAVE_SAMPLERATE
	src_delete(info);
	if(ios->data_in!=0)
		free(ios->data_in);
	if(ios->data_out!=0)
		free(ios->data_out);
	free(ios);
	#endif
}

int Converter::inputsize() const {
	return insize;
}

void Converter::inputsize(int size){
	if(in!=0) 
		in=(double*)realloc(in, sizeof(double)*size);
	if(size>insize){
		infree=size-insize;
	}else{
		infree=0;
	}
	insize=size;
}

int Converter::outputsize() const {
	return outsize;
}

void Converter::outputsize(int size){
	if(out!=0)
		out=(double*)realloc(out, sizeof(double)*size );
	outsize=size;
}

double Converter::ratio() const {
	#ifdef HAVE_SAMPLERATE
	return ios->src_ratio;
	#endif
}

void Converter::ratio(double rat){
	#ifdef HAVE_SAMPLERATE
	src_set_ratio(info, rat);
	ios->src_ratio=rat;
	#endif
}

int Converter::usable() const {
	return infree;
}

int Converter::convert(double* inp,int isize,double* outp,int osize){ //convert
	if(isize > infree)
		return -1;
	if(osize > outsize)
		return -1;
	//ok we can start 
	if(in==0)
		in=(double*)malloc(sizeof(double)*insize);

	if(out==0)
		out = (double*)malloc(sizeof(double)*insize);

	memcpy(in+(insize-infree),inp, sizeof(double)*infree);
	//data filled. configuring ios
	#ifdef HAVE_SAMPLERATE
	if(ios->data_in==0)
		ios->data_in=(float*)malloc(sizeof(float)*insize);
	if(ios->data_out==0)
		ios->data_out=(float*)malloc(sizeof(float)*outsize);
	
	for(uint i=0;i< insize;i++){
		ios->data_in[i]=float(in[i]);
	}
	ios->input_frames=isize;
	ios->output_frames=osize;
	ios->end_of_input=0;
	src_process(info, ios);

	//ok data convert now we can fill out
	//did we have data waiting in out?
	if(outend>0 &&  outend+ios->output_frames_gen > osize){
		//we copy it in the outp
		memcpy(outp, out, sizeof(double)*outend);
	}
	int ret=0;
	if(ios->output_frames_gen + outend > osize)
		ret=osize;
	//is out > oframes?
	if(ios->output_frames_gen > osize-outend){
		//we need to memorise info
		for(uint i=0;i< osize-outend;i++){
			outp[i+outend]=double(ios->data_out[i]);
		}
		//now we memorise data...
		for(uint i=0;i< ios->output_frames_gen-(osize-outend);i++){
			out[i]=double(ios->data_out[osize-outend+i]);
		}
		outend=ios->output_frames_gen-(osize-outend);
	}else if(ios->output_frames_gen == osize-outend) {
		for(uint i=0;i<osize-outend;i++){
			outp[i+outend]=double(ios->data_out[i]);
		}
	}else{
		for(uint i=0;i< ios->output_frames_gen;i++){
			out[outend+i]=double(ios->data_out[i]);
		}
		outend+=ios->output_frames_gen;
	}
	std::cout << "End of out " << outend << std::endl;
	if(ios->input_frames_used < insize){
		//we don't have used all entry...
		//so we cpy used data to the beginning of the buffer.
		double tmp;
		for(uint i=0;i<insize-ios->input_frames_used;i++){
			tmp = in[i+ios->input_frames_used];
			in[i]=tmp;
			in[i+ios->input_frames_used]=.0;
		}
		infree=insize-ios->input_frames_used;
	}else{
		infree=insize;
	}	
	return ret;
	#endif
}
int Converter::convert(Buffer& inb, Buffer& outb){
	int isize=inb.size();
	int osize=outb.size();
	bool full=false;
	if(isize > infree){
		if(outend>0 ){
		//we copy it in the outp
			for(uint i=0;i<outend && i<outsize;i++){
				outb[i]=out[i];
			}
			int ret = outend;
			outend=0;
			return ret;
		}else{
			std::cerr << "No place in input buffer and no data to send" << std::endl;
		}
	}
	//ok we can start 

	if(in==0){
		in=(double*)malloc(sizeof(double)*insize);
		ios->data_in=(float*)malloc(sizeof(float)*insize);
	}

	if(out==0){
		out = (double*)malloc(sizeof(double)*outsize);
		ios->data_out=(float*)malloc(sizeof(float)*outsize);
	}
	std::cout <<" (isize: infree)" << isize << " : " << infree << std::endl;
	for(uint i=0;i<infree && i<isize;i++){
		in[insize-infree+i]=inb[i];
	}
	//now we need to deplace data in the buffer in
	if(isize > infree){
		for(int i=0;i<isize-infree;i++){
			inb[i]=inb[i+infree];
		}
		for(uint i=infree;i<isize;i++){
			inb[i]=0.;
		}
	}
	//data filled. configuring ios
	#ifdef HAVE_SAMPLERATE
	for(uint i=0;i< insize;i++){
		ios->data_in[i]=float(in[i]);
	}
	ios->input_frames=isize;
	ios->output_frames=osize;
	ios->end_of_input=0;
	int error = src_process(info, ios);
	std::cout << src_strerror(error) << " " << ios->input_frames_used << " : " << ios->output_frames_gen << " : " << ios->src_ratio << std::endl;
	//ok data convert now we can fill out
	//did we have data waiting in out?
	if(outend>0 && outend+ios->output_frames_gen > osize){
		//we copy it in the outp
		for(uint i=0;i<outend && i<outsize;i++){
			outb[i]=out[i];
		}
	}
	int ret=0;
	if(ios->output_frames_gen + outend > osize){
		ret=osize;
	}else{
		ret = ios->output_frames_gen + outend;
	}
	//is out > oframes?
	if(ios->output_frames_gen > osize-outend){
		//we need to memorise info
		for(uint i=0;i< osize-outend;i++){
			outb[i+outend]=double(ios->data_out[i]);
		}
		//now we memorise data...
		std::cout << "memorise" << ios->output_frames_gen-(osize-outend) << std::endl;
		for(uint i=0;i< ios->output_frames_gen-(osize-outend);i++){
			out[i]=double(ios->data_out[osize-outend+i]);
		}
		outend=ios->output_frames_gen-(osize-outend);
	}else if( ios->output_frames_gen == osize-outend){
		for(uint i=0;i<osize-outend;i++){
			outb[i+outend]=double(ios->data_out[i]);
		}
		outend=0;
	}else{
		//we memorise data.
		for(uint i=0;i< ios->output_frames_gen;i++){
			outb[outend+i]=double(ios->data_out[i]);
		}
		outend=0;
	}
	std::cout << "End of out " << outend << std::endl;
	if(ios->input_frames_used < insize){
		//we don't have used all entry...
		//so we cpy used data to the beginning of the buffer.
		double tmp;
		for(uint i=0;i<insize-ios->input_frames_used;i++){
			tmp = in[i+ios->input_frames_used];
			in[i]=tmp;
			in[i+ios->input_frames_used]=.0;
		}
		infree=insize-ios->input_frames_used;
	}else{
		infree=insize;
	}	

	return ret;
	#endif
}

int Converter::pending() const {
	return outend;
}

}

